package it.infodea.tapestrydea.services.breadcrumb.impl;

import it.infodea.tapestrydea.services.breadcrumb.BreadcrumbService;
import it.infodea.tapestrydea.services.breadcrumb.bean.BreadcrumbBean;
import it.infodea.tapestrydea.state.BreadcrumbState;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.internal.services.PageActivationContextCollector;
import org.apache.tapestry5.internal.services.RequestPageCache;
import org.apache.tapestry5.internal.structure.Page;
import org.apache.tapestry5.services.ApplicationStateManager;
import org.apache.tapestry5.services.PageRenderLinkSource;

public class BreadcrumbServiceImpl implements BreadcrumbService {

	private PageRenderLinkSource linkFactory;

	private RequestPageCache pageCache;

	private PageActivationContextCollector contextCollector;

	private ApplicationStateManager applicationStateManager;

	public BreadcrumbServiceImpl(final PageRenderLinkSource linkFactory, final RequestPageCache pageCache,
			final PageActivationContextCollector contextCollector, final ApplicationStateManager applicationStateManager) {
		this.linkFactory = linkFactory;
		this.pageCache = pageCache;
		this.contextCollector = contextCollector;
		this.applicationStateManager = applicationStateManager;
	}

	public void addBreadcrumb(ComponentResources resources) {
		addBreadcrumb(resources, getCurrentPageName(resources), null, null);
	}

	public void addBreadcrumb(ComponentResources resources, String label) {
		addBreadcrumb(resources, getCurrentPageName(resources), null, label);
	}

	public void addBreadcrumb(ComponentResources resources, String breadcrumbId, String label) {
		addBreadcrumb(resources, getCurrentPageName(resources), breadcrumbId, label);
	}

	private void addBreadcrumb(ComponentResources resources, String currentPageName, String breadcrumbId, String label) {
		label = handleLabel(resources, currentPageName, label);
		BreadcrumbBean newBread = new BreadcrumbBean(getCurrentPageLink(currentPageName), breadcrumbId);
		adjustStackPosition(newBread);
		newBread.setLabel(label);
		addToStack(newBread);
	}

	private String handleLabel(ComponentResources resources, String currentPageName, String label) {
		if (label == null) {
			String pageNameLabelKey = getPageNameLabelKey(currentPageName);
			if (!resources.getMessages().contains(pageNameLabelKey + "-hidden")) {
				label = resources.getMessages().get(pageNameLabelKey);
			}
		}
		return label;
	}

	public void removeLast() {
		removeLatest();
	}

	public void reset() {
		getBcState().reset();
	}

	public Iterator<BreadcrumbBean> iterator() {
		return getStack().iterator();
	}

	/*PRIVATE*/

	private BreadcrumbState getBcState() {
		return applicationStateManager.get(BreadcrumbState.class);
	}

	private List<BreadcrumbBean> getStack() {
		return getBcState().getStack();
	}

	private Set<String> getBcSet() {
		return getBcState().getBcSet();
	}

	private void addToStack(BreadcrumbBean bread) {
		getBcSet().add(bread.getId());
		getStack().add(bread);
	}

	private BreadcrumbBean removeLatest() {
		int index = getStack().size() - 1;
		if (index < 0)
			return null;
		BreadcrumbBean bread = getStack().remove(index);
		String id = bread.getId();
		getBcSet().remove(id);
		return bread;
	}

	private String getPageNameLabelKey(String currentPageName) {
		String currentPageNameLabelKey = currentPageName.replace('/', '_') + "-breadcrumb-label";
		return currentPageNameLabelKey;
	}

	private void adjustStackPosition(BreadcrumbBean currentBread) {
		if (getBcSet().contains(currentBread.getId())) {
			BreadcrumbBean tmpBread;
			do {
				tmpBread = removeLatest();
			} while (tmpBread != null && !currentBread.equals(tmpBread));
		}
	}

	private Link getCurrentPageLink(String currentPageName) {
		Page currentPage = getCurrentPage(currentPageName);
		Object[] currentActivationContext = getCurrentActivationContext(currentPage);
		return getCurrentPageLink(currentPageName, currentActivationContext);
	}

	private Link getCurrentPageLink(String currentPageName, Object[] currentActivationContext) {
		return linkFactory.createPageRenderLinkWithContext(currentPageName, currentActivationContext);
	}

	private Object[] getCurrentActivationContext(Page currentPage) {
		return contextCollector.collectPageActivationContext(currentPage.getName());
	}

	private Page getCurrentPage(String currentPageName) {
		return pageCache.get(currentPageName);
	}

	private String getCurrentPageName(ComponentResources resources) {
		return resources.getPageName();
	}

}
